#!/usr/bin/python
# -*- coding: utf-8 -*-

##
#  SnapFly
#   - v0.5 : Fork ADesk Menu to SnapFly
#
#   by Drakmail <drakmail@gmail.com>
#   by NomerUNO <uno.kms@gmail.com>
#       for AgiliaLinux http://agilialinux.ru/
#
#  ADesk Menu
#   - v0.4 : Implemented many new features
#   - v0.3 : add --no-tray option, cairo rendering is composite
#   - v0.2 : add signal , get cursor position and show menu whith it
#   - v0.1 : check if 'Terminal=true' in .desktop file, always above other windows
#
#   by ADcomp <david.madbox@gmail.com>
#      http://www.ad-comp.be/
#
#   This program is distributed under the terms of the GNU General Public License
#   For more info see http://www.gnu.org/licenses/gpl.txt
##

import os
import sys
import gtk
import signal

import snapfly_core
import snapfly_core.ui

import gobject, dbus, dbus.service

from dbus.mainloop.glib import DBusGMainLoop

import pyinotify

gobject.threads_init()

wm = pyinotify.WatchManager()  # Watch Manager
mask = pyinotify.IN_DELETE | pyinotify.IN_CREATE  # watched events

home = os.getenv('HOME')
CONFIG_PATH = home + '/.config/snapfly/config'

class ConfigController():
    def __init__(self):
        self.default = {'terminal':'x-terminal-emulator',
                        'systray':'true',
                        'rounded':'4',
                        'menu_width':'430',
                        'bg_color':'#DCDCDC',
                        'border_color':'#4D4D4D',
                        'hide_list':'None',
                        }
        config = {}
        if os.access(CONFIG_PATH, os.F_OK|os.R_OK):
            print('Found user config ..')
            f = open(CONFIG_PATH,'r')
            for line in f:
                if line == '\n' or line.startswith('#'):
                    continue
                else:
                    line = line.strip('\n')
                    line = line.strip(' ')
                    line = line.split('=')
                    config[line[0]] = line[1]
            f.close()
        self.config = config
        self.validateConfig()

    #Checks, is string is integer
    def isInt(self,string):
        try:
            number = int(string.strip().split()[0])
        except (ValueError, IndexError):
            return False
        return True

    def validateConfig(self):
        for line in self.config:
            if line not in self.default:
                print("Error in config file: Unknown parameter ("+line+").")
                continue
            if self.config[line] == '':
                print("Error in config file: parameter "+line+" can't be empty. Using default value.")
                self.config[line] = self.default[line]
            if line == 'rounded' or line == 'menu_width':
                if not self.isInt(self.config[line]):
                    print("Error in config file: parameter "+line+" must be valid integer. Using default value.")
                    self.config[line] = self.default[line]
                if int(self.config[line]) < 0:
                    print("Error in config file: parameter "+line+" must be positive value. Using default value.")
                    self.config[line] = self.default[line]

    def getValue(self, parameter):
        if parameter in self.config:
            return self.config[parameter]
        else:
            return self.default[parameter]
 
class EventHandler(pyinotify.ProcessEvent):
    def process_IN_CREATE(self, event):
        global g_menu
        g_menu.update_menu()
    def process_IN_DELETE(self, event):
        global g_menu
        g_menu.update_menu()

handler = EventHandler()
notifier = pyinotify.ThreadedNotifier(wm, handler)
wdd = wm.add_watch('/usr/share/applications', mask, rec=True)
notifier.start()

class DbusHandler(dbus.service.Object):
    def __init__(self):
        bus_name = dbus.service.BusName('ru.agilialinux.openbox', bus=dbus.SessionBus())
        dbus.service.Object.__init__(self, bus_name, '/SnapFly')
 
    @dbus.service.method('ru.agilialinux.openbox')
    def toggle(self):
        global g_menu
        g_menu.callback_signal()
        return "Ok"

class Menu():
    def __init__(self):
        self.hide_me = False
        self.focus_check = False
        self.mode = None
        config = ConfigController()
        self.terminal = config.getValue('terminal')
        self.systray = config.getValue('systray')
        self.rounded = int(config.getValue('rounded'))
        self.menu_width = int(config.getValue('menu_width'))
        self.bg_color = config.getValue('bg_color')
        self.border_color = config.getValue('border_color')
        self.hideList = config.getValue('hide_list').split(',')

        if self.systray == "true":
            self.statusIcon = gtk.status_icon_new_from_file('images/SnapFly.png')
            self.statusIcon.set_visible(True)
            self.statusIcon.set_tooltip('SnapFly Menu')
            self.statusIcon.connect('popup-menu', self.status_icon_popup)
            self.statusIcon.connect('activate', self.toggle_hide)

            # Menu
            self.popupMenu = gtk.Menu()
            menuPopup = gtk.ImageMenuItem (gtk.STOCK_QUIT)
            menuPopup.connect("activate", self.doquit)
            self.popupMenu.add(menuPopup)
            self.popupMenu.show_all()

        self.create_window()
        self.toggle_hide()

    def doquit(self, widget=None, data=None):
        notifier.stop()
        gtk.main_quit()

    def run(self):
        try:
            gtk.main()
        except:
            notifier.stop()

    def update_menu(self):
        self.window.remove(self.nbook)
        self.nbook = gtk.Notebook()
        self.nbook.show()
        self.nbook.set_tab_pos(gtk.POS_LEFT)
        self.nbook.set_border_width(0)
        self.window.add(self.nbook)
        snapfly_core.MakeMenu(menu=None, notebook=self.nbook, callback_action=self.ExecuteAction, terminal=self.terminal, hideList=self.hideList).run()

    def create_window(self):
        bg_color_rgb = snapfly_core.hex2rgb(self.bg_color)
        border_color_rgb = snapfly_core.hex2rgb(self.border_color)
        self.window= snapfly_core.ui.PopupWindow(bg_color_rgb, border_color_rgb, self.rounded)
        self.window.add_events(gtk.gdk.FOCUS_CHANGE_MASK)
        self.window.connect("focus-out-event", self.lost_focus)
        self.window.connect("key-press-event", self.onkeypress)
        self.window.connect("destroy", self.destroy)
        self.window.set_size_request(self.menu_width,-1)

        self.nbook = gtk.Notebook()
        self.nbook.show()
        self.nbook.set_tab_pos(gtk.POS_LEFT)
        self.nbook.set_border_width(0)
        self.window.add(self.nbook)
        self.window.show_all()
        snapfly_core.MakeMenu(menu=None, notebook=self.nbook, callback_action=self.ExecuteAction, terminal=self.terminal, hideList=self.hideList).run()

    def ExecuteAction(self, widget, event, item):
        self.focus_check = False
        snapfly_core.launch_command(item[0])
        self.toggle_hide(widget)

    def toggle_hide(self, widget=None, event=None):
        if self.hide_me:
            self.hide_me = False
            self.show_menu(self.mode)
            self.window.show()
            self.window.present()
            self.focus_check = True
        else:
            self.hide_me = True
            self.focus_check = False
            self.window.hide()

    def show_menu(self, mode=None):
        screen_width, screen_height = gtk.gdk.screen_width(), gtk.gdk.screen_height()

        if not mode:
            (screen, rect, orientation) = self.statusIcon.get_geometry()
            if rect[0] + self.menu_width > screen_width:
                x = screen_width - self.menu_width
            else:
                x = rect[0] + self.menu_width
            y = rect[1]+rect[3]
        else:
            x, y = 0,0
            rootwin = self.window.get_screen().get_root_window()
            w_width , w_height = self.window.get_size()
            x, y, mods = rootwin.get_pointer()
            if x + self.menu_width > screen_width:
                x = screen_width - self.menu_width
            if y + w_height > screen_height:
                y = screen_height - w_height

        self.window.move(x, y)
        self.window.present()
        
    def lost_focus(self, widget, event):
        if self.focus_check:
            self.toggle_hide()

    def onkeypress(self, widget, event):
        if event.keyval == gtk.keysyms.Escape:
            self.toggle_hide()

    def status_icon_popup(self, widget, button, active_time):
        self.popupMenu.popup(None, None, None, 1, 0)

    def callback_signal(self):
        self.mode = "mouse"
        self.toggle_hide()
        self.mode = None

    def destroy(self):
        self.notifier.stop()

##--

global g_menu

def SigUSR1Handler(signum, frame):
    """ grab signal to show """
    global g_menu
    g_menu.callback_signal()
    return

def checkConfigs():
    if not os.path.exists("%s/.config/snapfly" % home):
         os.makedirs("%s/.config/snapfly" % home)

    if not os.path.exists("%s/.config/snapfly/config" % home):
         print "Writing default config..."
         config_file = open(home+'/.config/snapfly/config', 'w')
         config_file.write("# Sets terminal emulator for 'Run in terminal' programms\n")
         config_file.write("# terminal=x-terminal-emulator\n")
         config_file.write("# Show icon in systray \n")
         config_file.write("# systray=true\n")
         config_file.write("# Do rounded corners. You can set it value to 0 for much more faster drawing\n")
         config_file.write("# rounded=4\n")
         config_file.write("# Width of menu\n")
         config_file.write("# menu_width=430\n")
         config_file.write("# Background color\n")
         config_file.write("# bg_color=#DCDCDC\n")
         config_file.write("# Borders color\n")
         config_file.write("# border_color=#4D4D4D\n")
         config_file.write("# Hide GNOME or KDE or LXDE or ROX or XFCE or Old items from menu. Default: None\n")
         config_file.write("# hide_list=GNOME,KDE,Old\n")
         config_file.close()

    if not os.path.exists("%s/.config/snapfly/usermenu" % home):
         print "Writing default usermenu..."
         usermenu = open(home+'/.config/snapfly/usermenu', 'w')
         usermenu.write("# Usermenu config file format: \n")
         usermenu.write("# @CategoryName##/path/to/category/image.png \n")
         usermenu.write("# /path/to/command/in/CategoryName##/path/to/item/image.png##Name of item##Comment\n")
         usermenu.write("# /path/to/command2/in/CategoryName##/path/to/item2/image.png##Name of item2##Comment\n")
         usermenu.close()

def run():
    try:
        global g_menu

        checkConfigs()

        ## need to change directory
        SRC_PATH = os.path.dirname(os.path.realpath( __file__ ))
        os.chdir(SRC_PATH)

        g_menu = Menu()
        # change directory to Home
        os.chdir(os.getenv('HOME'))
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        calc = DbusHandler()
        g_menu.run()
    except:
        print "Error Accured. Exiting"
        notifier.stop()

if __name__ == "__main__":
    realpath = os.path.dirname(os.path.realpath( __file__ ))
    os.chdir(realpath)

    if len(sys.argv) >= 2:
        if sys.argv[1] == '-v':
            print "SnapFly Menu v%s.%s" % (adesk.VERSION, adesk.RC)
            notifier.stop()
        elif sys.argv[1] == '-c':
            CONFIG_PATH = sys.argv[2]
            run()
        else:
            print "Unknow option .."
            print "Usage : snapfly [-c|-v]"
            print " -v          : print version"
            print " -c CONFIG   : alternate config file"
            notifier.stop()
    else:
        run()
